home *** CD-ROM | disk | FTP | other *** search
/ Delphi 2 - Developers' Solutions / Delphi 2 Developers' Solutions.iso / dds / chap09 / howto06 / ccwsock2.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-02-15  |  35.6 KB  |  946 lines

  1. unit CCWSock2;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  7.   StdCtrls, Winsock;
  8. const
  9.   { This is the base message used by Winsock to notify of Winsock asynch act }
  10.   WM_ASYNCSELECT = WM_USER + 0;
  11. type
  12.   { These data types translate the c-like winsock unit names }
  13.   Server_Entry = TServEnt;
  14.   PServer_Entry = ^Server_Entry;
  15.   Protocol_Entry = TProtoEnt;
  16.   PProtocol_Entry = ^Protocol_Entry;
  17.   Internet_Address = TInAddr;
  18.   PInternet_address = ^Internet_Address;
  19.   Internet_Socket_Address = TSockAddrIn;
  20.   PInternet_Socket_Address = ^Internet_Socket_Address;
  21.   Host_Entry = THostEnt;
  22.   PHost_entry = ^Host_Entry;
  23.   Winsock_Implementation_Data = TWSAData;
  24.   Generic_Socket_Address = Internet_Socket_Address;
  25.   Socket_Protocol = TSockProto;
  26.   Lingering_Control = TLinger;
  27.   { These two event data types are used to hook into the Winsock Asynch system }
  28.   TWSAEvent = procedure( Sender : TObject; Socket : TSocket ) of object;
  29.   TWSAError = procedure( Sender     : TObject;
  30.                          ErrorCode  : Integer;
  31.                          TheMessage : String ) of object;
  32.   TCCSocket = class( TWinControl )
  33.   public
  34.     Socket_WSA_Data       : Winsock_Implementation_Data;
  35.     ErrorCode           : Integer;
  36.     FullErrorMessage    : string;
  37.     WinsockErrorMessage : string;
  38.     Socket_Server_Entry   : PServer_Entry;
  39.     Socket_Host_Entry     : Phost_entry;
  40.     Socket_Protocol_Entry : PProtocol_Entry;
  41.     Socket_IP_Address     : Internet_Socket_Address;
  42.     FPort_Name            : String;
  43.     FIP_Address_Name      : String;
  44.     FSocket               : TSocket;
  45.     FMasterSocket         : TSocket;
  46.     FBlockingMode         : Boolean;
  47.     FTimeoutValue         : Integer;
  48.     FOnDataIsAvailable    : TWSAEvent;
  49.     FOnDataCanBeSent      : TWSAEvent;
  50.     FOnOOBDataIsAvailable : TWSAEvent;
  51.     FOnSessionClosed      : TWSAEvent;
  52.     FOnSessionIsAvailable : TWSAEvent;
  53.     FOnSessionConnected   : TWSAEvent;
  54.     FOnErrorOccurred      : TWSAError;
  55.     procedure SetStringData( TheData: string );
  56.     function GetStringData          : string;
  57.     procedure SetStringDataOutOfBand( TheData: string );
  58.     function GetStringDataOutOfBand : string;
  59.     function PeekCurrentData        : string;
  60.     function GetSocketErrorDescription( ErrorCode : Integer) : string;
  61.     procedure SetSocketErrorData( SocketFunction : string );
  62.     procedure TWMPaint( var Msg : TWMPaint ); message WM_PAINT;
  63.     procedure ActivateNonAsynchTimeout;
  64.     procedure DeactivateNonAsynchTimeout;
  65.     procedure WMASyncSelect( var Msg : TMessage ); message WM_ASYNCSELECT;
  66.     procedure WMTimer( var Msg : TMessage ); message WM_TIMER;
  67.     constructor Create( AOwner : TComponent ); override;
  68.     destructor Destroy; override;
  69.     procedure CCSockConnect;
  70.     procedure CCSockClose;
  71.     procedure CCSockListen;
  72.     procedure CCSockCancelListen;
  73.     function CCSockReceive(     TheSocket     : TSocket;
  74.                                 TheTextBuffer : PChar;
  75.                             var TheTextLength : Integer
  76.                           ) : Integer;
  77.     function CCSockSend(    TheSocket     : TSocket;
  78.                             TheTextBuffer : PChar;
  79.                         var TheTextLength : Integer
  80.                        ) : Integer;
  81.     function CCSockAccept                                  : TSocket;
  82.     function GetSocketIPAddress( TheSocket: TSocket )      : string;
  83.     function GetSocketPort( TheSocket : TSocket )          : string;
  84.     function GetSocketPeerIPAddress( TheSocket : TSocket ) : string;
  85.     function GetSocketPeerPort( TheSocket : TSocket )      : string;
  86.     function SocketIsNotBlocking                           : Boolean;
  87.     procedure ActivateBlockingMode( BeginBlocking : Boolean );
  88.     property StringData      : string
  89.      read GetStringData write SetStringData;
  90.     property PeekData        : string
  91.      read PeekCurrentData;
  92.     property OutOfBand       : string
  93.      read GetStringDataOutOfBand write SetStringDataOutOfBand;
  94.     property TheSocket       : TSocket
  95.      read FSocket write FSocket;
  96.     property TheMasterSocket : TSocket
  97.      read FMasterSocket write FMasterSocket;
  98.   published
  99.     property IPAddressName        : string
  100.      read FIP_Address_Name write FIP_Address_Name;
  101.     property PortName             : string
  102.      read FPort_Name write FPort_Name;
  103.     property AsynchMode           : Boolean
  104.      read SocketIsNotBlocking write ActivateBlockingMode default True;
  105.     property NonAsynchTimeoutValue   : Integer
  106.      read FTimeoutValue write FTimeoutValue default 30;
  107.     property OnDataIsAvailable    : TWSAEvent
  108.      read FOnDataIsAvailable write FOnDataIsAvailable;
  109.     property OnOOBDataIsAvailable    : TWSAEvent
  110.      read FOnOOBDataIsAvailable write FOnOOBDataIsAvailable;
  111.     property OnDataCanBeSent    : TWSAEvent
  112.      read FOnDataCanBeSent write FOnDataCanBeSent;
  113.     property OnSessionClosed      : TWSAEvent
  114.      read FOnSessionClosed write FOnSessionClosed;
  115.     property OnSessionIsAvailable : TWSAEvent
  116.      read FOnSessionIsAvailable write FOnSessionIsAvailable;
  117.     property OnSessionConnected   : TWSAEvent
  118.      read FOnSessionConnected write FOnSessionConnected;
  119.     property OnErrorOccurred      : TWSAError
  120.      read FOnErrorOccurred write FOnErrorOccurred;
  121.   end;
  122.   CCHost_Entry = packed record
  123.     Host_Name              : PChar;
  124.     Host_Aliases           : ^PChar;
  125.     Host_Address_Type      : smallint;
  126.     Host_Address_Length    : smallint;
  127.     Case Integer of        { Another useful variant record    }
  128.     0: ( host_address_list : ^PChar ); { Double pointer again }
  129.     1: ( host_address      : ^PInternet_address );
  130.   end;
  131.  
  132. implementation
  133.  
  134. { This is the override create method for the socket component }
  135. constructor TCCSocket.Create( AOwner : TComponent );
  136. var
  137.   ReturnCode : Integer; { Used to signal error }
  138. begin
  139.   { Call inherited first! }
  140.   inherited Create( AOwner );
  141.   { Enable Asynch mode since in Windows }
  142.   FBlockingMode := false;
  143.   { Set Timeout for asynch ops }
  144.   FTimeoutValue := 30;
  145.   { Set up no sockets in the two native vars }
  146.   FSocket := INVALID_SOCKET;
  147.   FMasterSocket := INVALID_SOCKET;
  148.   { Start up Winsock }
  149.   ReturnCode := WSAStartup( $101 , Socket_WSA_Data );
  150.   { If don't get 0 store the error code }
  151.   if ReturnCode <> 0 then SetSocketErrorData( 'Constructor (WSAStartup)' );
  152. end;
  153.  
  154. { This is the destroy override method }
  155. destructor TCCSocket.Destroy;
  156. var
  157.   ReturnCode : Integer; { Holds possible error code }
  158. begin
  159.   { Attempt to shut down winsock }
  160.   ReturnCode := WSACleanup;
  161.   { If didn't get 0 save the error }
  162.   if ReturnCode < 0 then SetSocketErrorData( 'Destructor (WSACleanup)' );
  163.   { call inherited }
  164.   inherited Destroy;
  165. end;
  166.  
  167. { This is just used to draw the nonvisual element during design time }
  168. procedure TCCSocket.TWMPaint( var Msg : TWMPaint );
  169. var
  170.   TheIcon : HIcon; { Internal icon }
  171.   TheDC   : HDC;   { Internal dc   }
  172. begin
  173.   { If in design mode draw the icon }
  174.   if csDesigning in ComponentState then
  175.   begin
  176.     { Load the icon from the instance via the DCR file }
  177.     TheIcon := LoadIcon( HInstance , MAKEINTRESOURCE( 'TCCSocket' ));
  178.     { Get a device context }
  179.     TheDC := GetDC( Handle );
  180.     { Set the internal width to that of an icon }
  181.     Width := 32;
  182.     Height := 32;
  183.     { Display the icon }
  184.     DrawIcon( TheDC , 0 , 0 , TheIcon );
  185.     { Get rid of the evidence }
  186.     ReleaseDC( Handle , TheDC );
  187.     FreeResource( TheIcon );
  188.   end;
  189.   { Let Windows know drawing is done }
  190.   ValidateRect( Handle , nil );
  191. end;
  192.  
  193. { Function to return Asynch mode }
  194. function TCCSocket.SocketIsNotBlocking: Boolean;
  195. begin
  196.   { return inverse of blocking mode }
  197.   SocketIsNotBlocking := not FBlockingMode;
  198. end;
  199.  
  200. { This turns off asynch mode via inverse of parameter }
  201. procedure TCCSocket.ActivateBlockingMode( BeginBlocking: Boolean );
  202. begin
  203.   FBlockingMode := not BeginBlocking;
  204. end;
  205.  
  206. { This is a full access method to send a string over the socket }
  207. procedure TCCSocket.SetStringData( TheData : string );
  208. var
  209.   BytesLeftToSend   ,                         { Counter for remaining data }
  210.   BytesSentSoFar    : Integer;                { Counter for sent data      }
  211.   DataBuffer        : array[0..256] of char;  { Buffer for string          }
  212.   DataBufferPointer : PChar;                  { Pointer to buffer          }
  213. begin
  214.   { Copy string into char array }
  215.   StrPCopy( DataBuffer , TheData );
  216.   { Move the pointer to the array's first element into the PChar }
  217.   DataBufferPointer := @DataBuffer[ 0 ];
  218.   { Count the total chars to send }
  219.   BytesLeftToSend := Length( TheData );
  220.   { Run a loop to send the string over the socket }
  221.   while BytesLeftToSend > 0 do
  222.   begin
  223.     { Start a timeout timer if not in blocking mode }
  224.     if not FBlockingMode then ActivateNonAsynchTimeout;
  225.     { Send some bytes over the net }
  226.     BytesSentSoFar := send( FSocket , DataBufferPointer , BytesLeftToSend , 0 );
  227.     { End timeout timer if not blocking }
  228.     if not FBlockingMode then DeactivateNonAsynchTimeout;
  229.     { If get a negative response code then signal error }
  230.     if BytesSentSoFar < 0 then
  231.     begin
  232.       { Save the error data }
  233.       SetSocketErrorData( 'SetStringData (Send)' );
  234.     end
  235.     else
  236.     begin
  237.       { Decrement total bytes left to send }
  238.       BytesLeftToSend := BytesLeftToSend - BytesSentSoFar;
  239.       { Increment pointer into the string }
  240.       DataBufferPointer := DataBufferPointer + BytesSentSoFar;
  241.     end;
  242.   end;
  243. end;
  244.  
  245. { This is a full access method to read a string from the socket }
  246. function TCCSocket.GetStringData: string;
  247. var
  248.   TheDataLength     : Integer; { Length of data received }
  249.   DataBuffer        : string;  { String to store data in }
  250.   ThePC             : PChar;
  251. begin
  252.   { If the socket has been set up try to get some data }
  253.   if FSocket <> INVALID_SOCKET then
  254.   begin
  255.     GetMem( ThePC , 256 );
  256.     { Activate timeout timer if not in blocking mode }
  257.     if not FBlockingMode then ActivateNonAsynchTimeout;
  258.     { Do a receive on any data waiting at the socket }
  259.     TheDataLength := recv( FSocket , ThePC , 255 , 0 );
  260.     { If not blocking kill timeout timer }
  261.     if not FBlockingMode then DeactivateNonAsynchTimeout;
  262.     { If negative data length then set error }
  263.     if TheDataLength < 0 then
  264.     begin
  265.       { Set the socket error conditions }
  266.       SetSocketErrorData( 'GetStringData (Recv)' );
  267.       { Return nothing }
  268.       Result := '';
  269.     end
  270.     else
  271.     begin
  272.       DataBuffer := StrPas( ThePC );
  273.       { And return the prepared string as result }
  274.       Result := DataBuffer;
  275.     end;
  276.     FreeMem( ThePC , 256 );
  277.   end
  278.   else Result := ''; { Return empty string if invalid socket }
  279. end;
  280.  
  281. { This is a full access method to send a string as OOB data }
  282. procedure TCCSocket.SetStringDataOutOfBand( TheData: string );
  283. var
  284.   BytesLeftToSend   ,                         { Counter for remaining data }
  285.   BytesSentSoFar    : Integer;                { Counter for sent data      }
  286.   DataBuffer        : array[0..256] of char;  { Buffer for string          }
  287.   DataBufferPointer : PChar;                  { Pointer to buffer          }
  288. begin
  289.   { Copy string into char array }
  290.   StrPCopy( DataBuffer , TheData );
  291.   { Move the pointer to the array's first element into the PChar }
  292.   DataBufferPointer := @DataBuffer[ 0 ];
  293.   { Count the total chars to send }
  294.   BytesLeftToSend := Length( TheData );
  295.   { Run a loop to send the string over the socket }
  296.   while BytesLeftToSend > 0 do
  297.   begin
  298.     { Start a timeout timer if not in blocking mode }
  299.     if not FBlockingMode then ActivateNonAsynchTimeout;
  300.     { Send some bytes over the net }
  301.     BytesSentSoFar := send( FSocket , DataBufferPointer ,
  302.                             BytesLeftToSend , MSG_OOB );
  303.     { End timeout timer if not blocking }
  304.     if not FBlockingMode then DeactivateNonAsynchTimeout;
  305.     { If get a negative response code then signal error }
  306.     if BytesSentSoFar < 0 then
  307.     begin
  308.       { Save the error data }
  309.       SetSocketErrorData( 'SetStringDataOutOfBand (Send)' );
  310.     end
  311.     else
  312.     begin
  313.       { Decrement total bytes left to send }
  314.       BytesLeftToSend := BytesLeftToSend - BytesSentSoFar;
  315.       { Increment pointer into the string }
  316.       DataBufferPointer := DataBufferPointer + BytesSentSoFar;
  317.     end;
  318.   end;
  319. end;
  320.  
  321. { This is a full access method to receive out of band data as a string }
  322. function TCCSocket.GetStringDataOutOfBand: string;
  323. var
  324.   TheDataLength     : Integer; { Length of data received }
  325.   DataBuffer        : string;  { String to store data in }
  326.   ThePC             : PChar;
  327. begin
  328.   { If the socket has been set up try to get some data }
  329.   if FSocket <> INVALID_SOCKET then
  330.   begin
  331.     GetMem( ThePC , 256 );
  332.     { Activate timeout timer if not in blocking mode }
  333.     if not FBlockingMode then ActivateNonAsynchTimeout;
  334.     { Do a receive on any data waiting at the socket }
  335.     TheDataLength := recv( FSocket , ThePC , 255 , MSG_OOB );
  336.     { If not blocking kill timeout timer }
  337.     if not FBlockingMode then DeactivateNonAsynchTimeout;
  338.     { If negative data length then set error }
  339.     if TheDataLength < 0 then
  340.     begin
  341.       { Set the socket error conditions }
  342.       SetSocketErrorData( 'GetStringDataOutOfBand (Recv)' );
  343.       { Return nothing }
  344.       Result := '';
  345.     end
  346.     else
  347.     begin
  348.       DataBuffer := StrPas( ThePC );
  349.       { And return the prepared string as result }
  350.       Result := DataBuffer;
  351.     end;
  352.     FreeMem( ThePC , 256 );
  353.   end
  354.   else Result := ''; { Return empty string if invalid socket }
  355. end;
  356.  
  357. function TCCSocket.PeekCurrentData: string;
  358. var
  359.   TheDataLength     : Integer; { Length of data received }
  360.   DataBuffer        : string;  { String to store data in }
  361.   ThePC             : PChar;
  362. begin
  363.   { If the socket has been set up try to get some data }
  364.   if FSocket <> INVALID_SOCKET then
  365.   begin
  366.     GetMem( ThePC , 256 );
  367.     { Activate timeout timer if not in blocking mode }
  368.     if not FBlockingMode then ActivateNonAsynchTimeout;
  369.     { Do a receive on any data waiting at the socket }
  370.     TheDataLength := recv( FSocket , ThePC , 255 , MSG_PEEK );
  371.     { If not blocking kill timeout timer }
  372.     if not FBlockingMode then DeactivateNonAsynchTimeout;
  373.     { If negative data length then set error }
  374.     if TheDataLength < 0 then
  375.     begin
  376.       { Set the socket error conditions }
  377.       SetSocketErrorData( 'PeekCurrentData (PeekData)' );
  378.       { Return nothing }
  379.       Result := '';
  380.     end
  381.     else
  382.     begin
  383.       DataBuffer := StrPas( ThePC );
  384.       { And return the prepared string as result }
  385.       Result := DataBuffer;
  386.     end;
  387.     FreeMem( ThePC , 256 );
  388.   end
  389.   else Result := ''; { Return empty string if invalid socket }
  390. end;
  391.  
  392. { This is a full access method to get the port id for a given socket }
  393. function TCCSocket.GetSocketPort( TheSocket : TSocket ) : string;
  394. var
  395.   TheAddress       : Internet_Socket_Address;  { Hold address info     }
  396.   TheAddressLength : Integer;                  { Hold addr info length }
  397. begin
  398.   { Find out the size of the structure }
  399.   TheAddressLength := SizeOf( TheAddress );
  400.   { Call the winsock dll routine }
  401.   getsockname( TheSocket , TheAddress , TheAddressLength );
  402.   { Pull off the properly-byte-ordered port number as a string }
  403.   Result := IntToStr( ntohs( TheAddress.sin_port ));
  404. end;
  405.  
  406. { This is a full access method to get the IP Address of a given socket }
  407. function TCCSocket.GetSocketIPAddress( TheSocket : TSocket ) : string;
  408. var
  409.   TheAddress       : Internet_Socket_Address; { Holds address info   }
  410.   TheAddressLength : Integer;                 { Holds size of info   }
  411.   AddressPChar     : PChar;                   { holds converted info }
  412. begin
  413.   { Get the size of the address record }
  414.   TheAddressLength := SizeOf( TheAddress );
  415.   { Call the Winsock DLL function }
  416.   getsockname( TheSocket , TheAddress , TheAddressLength );
  417.   { Make the conversion from 32 bit to dotted decimal }
  418.   AddressPChar := inet_ntoa( TheAddress.sin_addr );
  419.   { return it as a pascal string }
  420.   Result := StrPas( AddressPChar );
  421. end;
  422.  
  423. { This is a full access method to get the port number of the other end of a socket }
  424. function TCCSocket.GetSocketPeerPort( TheSocket : TSocket ) : string;
  425. var
  426.   TheAddress       : Internet_Socket_Address;  { Hold address info     }
  427.   TheAddressLength : Integer;                  { Hold addr info length }
  428. begin
  429.   { Find out the size of the structure }
  430.   TheAddressLength := SizeOf( TheAddress );
  431.   { Call the winsock dll routine }
  432.   getpeername( TheSocket , TheAddress , TheAddressLength );
  433.   { Pull off the properly-byte-ordered port number as a string }
  434.   Result := IntToStr( ntohs( TheAddress.sin_port ));
  435. end;
  436.  
  437. { This is a full access method to get the ip address of the other end of a socket }
  438. function TCCSocket.GetSocketPeerIPAddress(TheSocket: TSocket): string;
  439. var
  440.   TheAddress       : Internet_Socket_Address; { Holds address info   }
  441.   TheAddressLength : Integer;                 { Holds size of info   }
  442.   AddressPChar     : PChar;                   { holds converted info }
  443. begin
  444.   { Get the size of the address record }
  445.   TheAddressLength := SizeOf( TheAddress );
  446.   { Call the Winsock DLL function }
  447.   getpeername( TheSocket , TheAddress , TheAddressLength );
  448.   { Make the conversion from 32 bit to dotted decimal }
  449.   AddressPChar := inet_ntoa( TheAddress.sin_addr );
  450.   { return it as a pascal string }
  451.   Result := StrPas( AddressPChar );
  452. end;
  453.  
  454. { This is a full access method to receive a PChar of up to 64K of data at once }
  455. function TCCSocket.CCSockReceive(    TheSocket     : TSocket;
  456.                                      TheTextBuffer : PChar;
  457.                                  var TheTextLength : Integer
  458.                                 ) : Integer;
  459. begin
  460.   { If not an invalid socket then do the receive }
  461.   if FSocket <> INVALID_SOCKET then
  462.   begin
  463.     { If not in block mode then activate timeout timer }
  464.     if not FBlockingMode then ActivateNonAsynchTimeout;
  465.     { Return the direct result of the recv call into Winsock }
  466.     Result := recv( TheSocket , TheTextBuffer , TheTextLength , 0 );
  467.     { If not blocking kill timeout timer }
  468.     if not FBlockingMode then DeactivateNonAsynchTimeout;
  469.     { If negative length then get error info }
  470.     if TheTextLength < 0 then SetSocketErrorData( 'CCSockReceive' );
  471.   end
  472.   else Result := -1; { Return invalid PChar if not valid socket }
  473. end;
  474.  
  475. { This is a full access method to send a PChar of up to 64K of data at once }
  476. function TCCSocket.CCSockSend(    TheSocket     : TSocket;
  477.                                   TheTextBuffer : PChar;
  478.                               var TheTextLength : Integer
  479.                              ) : Integer;
  480. begin
  481.   { If not blocking then activate timeout timer }
  482.   if not FBlockingMode then ActivateNonAsynchTimeout;
  483.   { Send the info through raw }
  484.   TheTextLength := send( TheSocket , TheTextBuffer , TheTextLength , 0 );
  485.   { if not blocking then deactivate timeout timer }
  486.   if not FBlockingMode then DeactivateNonAsynchTimeout;
  487.   { if error code then get winsock error status }
  488.   if TheTextLength < 0 then SetSocketErrorData( 'CCSockSend' );
  489.   { return SOCKET_ERROR or number of bytes sent }
  490.   Result := TheTextLength;
  491. end;
  492.  
  493. { This method handles Asynchronous Windows messages for the Winsock }
  494. procedure TCCSocket.WMASyncSelect( var Msg : TMessage );
  495. begin
  496.   { The low word of the lParam field of the Msg is the event code }
  497.   case LoWord( Msg.lParam ) of
  498.     { This indicates data is available for reading on the socket }
  499.     FD_READ : begin
  500.                 if Assigned( FOnDataIsAvailable ) then
  501.                  FOnDataIsAvailable( Self , Msg.wParam ); { wParam = socket ID }
  502.               end;
  503.     { This indicates data is available for sending on the socket }
  504.     FD_WRITE : begin
  505.                 if Assigned( FOnDataCanBeSent ) then
  506.                  FOnDataCanBeSent( Self , Msg.wParam );
  507.               end;
  508.     { This indicates OOB data is available for reading on the socket }
  509.     FD_OOB : begin
  510.                 if Assigned( FOnOOBDataIsAvailable ) then
  511.                  FOnOOBDataIsAvailable( Self , Msg.wParam );
  512.               end;
  513.     { This indicates the socket has an incoming connection for accept }
  514.     FD_ACCEPT : begin
  515.                   if Assigned( FOnSessionIsAvailable ) then
  516.                    FOnSessionIsAvailable( Self , Msg.wParam );
  517.                 end;
  518.     { This indicates an outgoing connection has been accepted by peer }
  519.     FD_CONNECT: begin
  520.                   if Assigned( FOnSessionConnected ) then
  521.                    FOnSessionConnected( Self , Msg.wParam );
  522.                 end;
  523.     { This indicates the socket has been closed; presumably by peer }
  524.     FD_CLOSE : begin
  525.                  if Assigned( FOnSessionClosed ) then
  526.                   FOnSessionClosed( Self , Msg.wParam );
  527.                end;
  528.   end;
  529. end;
  530.  
  531. { This handles Asynchronous Timeouts gracefully }
  532. procedure TCCSocket.WMTimer( var Msg : TMessage );
  533. begin
  534.   { Kill a running timer }
  535.   KillTimer( Handle , 10 );
  536.   { If the socket is blocking then deal with timeout }
  537.   if WSAIsBlocking then
  538.   begin
  539.     { Cancel the blocking operation }
  540.     WSACancelBlockingCall;
  541.     { Return blocking call timeout error message }
  542.     if Assigned( FOnErrorOccurred ) then
  543.       FOnErrorOccurred( Self , WSAETIMEDOUT , 'Blocking call timed out' );
  544.   end;
  545. end;
  546.  
  547. { This is a wrapper method around the complexity of connecting a socket }
  548. procedure TCCSocket.CCSockConnect;
  549. var
  550.   ReturnCode : Integer;                    { Generic return code var }
  551.   TcpPChar   : PChar;                      { Boilerplate TCP string  }
  552.   PortName   : array[ 0 .. 31 ] of char;   { PChar for port name     }
  553.   DataBuffer : array[ 0 .. 256 ] of char;  { Generic buffer PChar    }
  554.   DummyValue : longint;                    { Must use variable call  }
  555.   The_Socket_Host_Entry : CCHost_Entry;
  556. begin
  557.   { No port name set error }
  558.   if FPort_Name = '' then
  559.   begin
  560.     SetSocketErrorData( 'No Valid Port Name in CCSockConnect');
  561.     exit;
  562.   end;
  563.   { No IP address set error }
  564.   if FIP_Address_Name = '' then
  565.   begin
  566.     SetSocketErrorData( 'No Valid IP Address in CCSockConnect');
  567.     exit;
  568.   end;
  569.   { Set required family value }
  570.   Socket_IP_Address.sin_family := AF_INET;
  571.   { Move the port name into the PChar }
  572.   StrPCopy( PortName , FPort_Name );
  573.   { Set up the boilerplate pchar }
  574.   TcpPChar := 'tcp';
  575.   { Do blocking call on server }
  576.   Socket_Server_Entry := getservbyname( PortName , TcpPChar );
  577.   { If no reply then use default from name }
  578.   if Socket_Server_Entry = nil then
  579.   begin
  580.     Socket_IP_Address.sin_port := htons( StrToInt( StrPas( PortName )));
  581.   end
  582.   else
  583.   begin
  584.     { Otherwise use the replied value }
  585.     Socket_IP_Address.sin_port := Socket_Server_Entry^.s_port;
  586.   end;
  587.   { Move the IP address into the data buffer }
  588.   StrPCopy( DataBuffer , FIP_Address_Name );
  589.   { Turn it into a real IP address in binary form }
  590.   Socket_IP_Address.sin_addr.s_addr :=
  591.    inet_addr( DataBuffer );
  592.   { If not found then do remote lookup }
  593.   if Socket_IP_Address.sin_addr.s_addr = INADDR_NONE then
  594.   begin
  595.     { Call blocking function on IP name }
  596.     Socket_Host_Entry := gethostbyname( DataBuffer );
  597.     { If still no good then error out and exit }
  598.     if Socket_Host_Entry = nil then
  599.     begin
  600.       SetSocketErrorData( 'Cannot convert host address in CCSockConnect');
  601.       exit;
  602.     end;
  603.     { Otherwise get the address }
  604.     The_Socket_Host_Entry.Host_Address_List := Socket_Host_Entry^.h_addr_list;
  605.     Socket_IP_Address.sin_addr := The_Socket_Host_Entry.Host_Address^^ ;
  606.   end;
  607.   { Do protocol acquisition via blocking call }
  608.   Socket_Protocol_Entry := getprotobyname( TcpPChar );
  609.   { Create a socket }
  610.   FSocket := socket( PF_INET ,
  611.                      SOCK_STREAM ,
  612.                      Socket_Protocol_Entry^.p_proto );
  613.   { If error code then exit with value set }
  614.   if FSocket < 0 then
  615.   begin
  616.     SetSocketErrorData('CCSockConnect (socket)');
  617.     exit;
  618.   end;
  619.   { If asynchmode then setup for asynch calls }
  620.   if not FBlockingMode then
  621.   begin
  622.     { Do ass call and allow all callback states; note this will }
  623.     { send a message when connected.                            }
  624.     ReturnCode := WSAASyncSelect( FSocket , Handle , WM_ASYNCSELECT ,
  625.       FD_READ or FD_WRITE or FD_OOB or FD_CLOSE or FD_CONNECT );
  626.     { If get error say so }
  627.     if ReturnCode <> 0 then SetSocketErrorData( 'WSAAsyncSelect' );
  628.   end
  629.   else
  630.   begin
  631.     { Otherwise set blocking mode }
  632.     DummyValue := 0;
  633.     ReturnCode := ioctlsocket( FSocket , FIONBIO , DummyValue );
  634.     { Set up timeout on blocking call }
  635.     ActivateNonAsynchTimeout;
  636.     { Attempt blocking connect }
  637.     ReturnCode := connect( FSocket ,
  638.                            Socket_IP_Address ,
  639.                            SizeOf( Socket_IP_Address ));
  640.     { Deactivate timeout on blocking call }
  641.     DeactivateNonAsynchTimeout;
  642.     { If any other error than WouldBlock signal connection error }
  643.     if ReturnCode <> 0 then
  644.     begin
  645.       ReturnCode := WSAGetLastError;
  646.       if ReturnCode <> WSAEWOULDBLOCK then
  647.        SetSocketErrorData( 'CCSockConnect' );
  648.     end;
  649.   end;
  650. end;
  651.  
  652. { This is a method to set the socket to a listening mode (ie server) }
  653. procedure TCCSocket.CCSockListen;
  654. const
  655.   DummyValue : Longint = 0;
  656. var
  657.   ReturnCode : Integer;
  658.   TcpPChar   : PChar;
  659.   PortName   : array[0..31] of char;
  660.   { szData: array[0..256] of char;}
  661. begin
  662.   { Invalid Port Name error }
  663.   if FPort_Name = '' then
  664.   begin
  665.     SetSocketErrorData( 'No Port Specified in CCSockListen' );
  666.     exit;
  667.   end;
  668.   { Set default AF_INET family }
  669.   Socket_IP_Address.sin_family := AF_INET;
  670.   { Set any IP Address }
  671.   Socket_IP_Address.sin_addr.s_addr := INADDR_ANY;
  672.   { Set default TCP string }
  673.   TcpPChar := 'tcp';
  674.   { Create PChar of port name }
  675.   StrPCopy( PortName , FPort_Name );
  676.   { Use blocking call to get server }
  677.   Socket_Server_Entry := getservbyname( PortName , TcpPChar );
  678.   { If no entry the use default number otherwise use returned one }
  679.   if Socket_Server_Entry = nil then
  680.      Socket_IP_Address.sin_port := htons( StrToInt( StrPas( PortName )))
  681.   else Socket_IP_Address.sin_port := Socket_Server_Entry^.s_port;
  682.   { Use blocking call to get protocol }
  683.   Socket_Protocol_Entry := getprotobyname( TcpPChar );
  684.   { Set up the server socket }
  685.   FMasterSocket := socket( PF_INET     ,
  686.                            SOCK_STREAM ,
  687.                            Socket_Protocol_Entry^.p_proto );
  688.   { If socket error return code and exit }
  689.   if FMasterSocket < 0 then
  690.   begin
  691.     SetSocketErrorData( 'socket' );
  692.     exit;
  693.   end;
  694.   { Bind the server socket }
  695.   ReturnCode := bind( FMasterSocket ,
  696.                       Socket_IP_Address,
  697.                       SizeOf( Socket_IP_Address ));
  698.   { If socket error then signal and exit }
  699.   if ReturnCode <> 0 then
  700.   begin
  701.     SetSocketErrorData( 'Bind' );
  702.     exit;
  703.   end;
  704.   { Do a listen call to set up waiting state }
  705.   ReturnCode := listen( FMasterSocket , 5 );
  706.   { If socket error then signal and exit }
  707.   if ReturnCode <> 0 then
  708.   begin
  709.     SetSocketErrorData( 'Listen' );
  710.     exit;
  711.   end;
  712.   { If not blocking do asynch call }
  713.   if not FBlockingMode then
  714.   begin
  715.     { Set up asynch call }
  716.     ReturnCode := WSAASyncSelect( FMasterSocket  ,
  717.                                   Handle         ,
  718.                                   WM_ASYNCSELECT ,
  719.                                   FD_READ or FD_WRITE or FD_OOB
  720.                                    or FD_ACCEPT or FD_CLOSE );
  721.     { If error then signal }
  722.     if ReturnCode <> 0 then SetSocketErrorData('WSAASyncSelect');
  723.   end
  724.   else ioctlsocket( FMasterSocket , FIONBIO , DummyValue ); { otherwise set blocking }
  725. end;
  726.  
  727. { This method terminates a listening mode (server) }
  728. procedure TCCSocket.CCSockCancelListen;
  729. var
  730.   ReturnCode : Integer; { status code var }
  731. begin
  732.   { if not blocking then turn off asynch mode }
  733.   if not FBlockingMode then
  734.     WSAASyncSelect( FMasterSocket , Handle , WM_ASYNCSELECT , 0 );
  735.   { Shutdown call }
  736.   shutdown( FMasterSocket , 2 );
  737.   { Close the socket }
  738.   ReturnCode := closesocket( FMasterSocket );
  739.   { If socket error signal it }
  740.   if ReturnCode <> 0 then
  741.     SetSocketErrorData( 'CancelListen (closesocket)' );
  742.   { kill socket id }
  743.   FMasterSocket := 0;
  744. end;
  745.  
  746. { This is the blocking mode accept procedure }
  747. function TCCSocket.CCSockAccept: TSocket;
  748. const
  749.   DummyValue : Longint = 0;
  750. var
  751.   ReturnCode    : Integer; { status code }
  752.   TheDataLength : Integer; { data length }
  753. begin
  754.   { Get length of the address variable }
  755.   TheDataLength := sizeof( Socket_IP_Address );
  756.   { if blocking then do timeout }
  757.   if FBlockingMode then ActivateNonAsynchTimeout;
  758.   { Do blocking accept call }
  759.   FSocket := accept( FMasterSocket     ,
  760.                      Socket_IP_Address ,
  761.                      TheDataLength       );
  762.   { If blocking }
  763.   if FBlockingMode then
  764.   begin
  765.     { Kill timeout timer }
  766.     DeactivateNonAsynchTimeout;
  767.     { Turn on blocking on accepted socket }
  768.     ioctlsocket( FSocket , FIONBIO , DummyValue );
  769.   end;
  770.   { If no accept then signal error }
  771.   if FSocket < 0 then SetSocketErrorData( 'Accept' );
  772.   { Return Socket ID }
  773.   Result := FSocket;
  774. end;
  775.  
  776. { Close a socket in either mode }
  777. procedure TCCSocket.CCSockClose;
  778. var
  779.   ReturnCode   : Integer;            { status code var }
  780.   LingerRecord : Lingering_Control;  { linger var      }
  781.   LingerArray  : array[ 0 .. 3 ] of char absolute LingerRecord;
  782.                                      { pointer into la }
  783. begin
  784.   { If not blocking then turn of asynch messaging }
  785.   if not FBlockingMode then
  786.     WSAASyncSelect( FSocket , Handle , WM_ASYNCSELECT , 0 );
  787.   { cancel any blocking }
  788.   if WSAIsBlocking then WSACancelBlockingCall;
  789.   { shut down the socket }
  790.   shutdown( FSocket , 2 );
  791.   { Set up the linger record }
  792.   LingerRecord.l_onoff := 1;
  793.   LingerRecord.l_linger := 0;
  794.   { Set up the linger status via setsockopt }
  795.   setsockopt( FSocket     ,
  796.               SOL_SOCKET  ,
  797.               SO_LINGER   ,
  798.               LingerArray ,
  799.               sizeof( LingerRecord ));
  800.   { Do the close call }
  801.   ReturnCode := closesocket( FSocket );
  802.   { signal error if one happens }
  803.   if ReturnCode <> 0 then SetSocketErrorData( 'Disconnect (closesocket)' );
  804.   { set socket to invalid value }
  805.   FSocket := INVALID_SOCKET;
  806. end;
  807.  
  808. { This sets up internal values for retrieval in case errors occur }
  809. procedure TCCSocket.SetSocketErrorData( SocketFunction : string );
  810. begin
  811.   { Get any winsock error }
  812.   ErrorCode := WSAGetLastError;
  813.   { Get text description of error }
  814.   WinsockErrorMessage := GetSocketErrorDescription( ErrorCode );
  815.   { Setup full error message for user friendliness }
  816.   if WinsockErrorMessage <> 'No Error' then
  817.    FullErrorMessage := 'Error '+ WinsockErrorMessage +
  818.     ' in function ' + SocketFunction else FullErrorMessage :=
  819.      SocketFunction;
  820.   { call error event handler }
  821.   if Assigned( FOnErrorOccurred ) then
  822.     FOnErrorOccurred( Self , ErrorCode , FullErrorMessage );
  823. end;
  824.  
  825. { Boilerplate error descriptions }
  826. function TCCSocket.GetSocketErrorDescription( ErrorCode : Integer ) : string;
  827. begin
  828.   case ErrorCode of
  829.     WSAEINTR:
  830.       GetSocketErrorDescription := 'System Interrupt Failure';
  831.     WSAEBADF:
  832.       GetSocketErrorDescription := 'Bad File Failure';
  833.     WSAEACCES:
  834.       GetSocketErrorDescription := 'File Permission Denied Failure';
  835.     WSAEFAULT:
  836.       GetSocketErrorDescription := 'Bad IP Address Failure';
  837.     WSAEINVAL:
  838.       GetSocketErrorDescription := 'Invalid Winsock API Call Argument Failure';
  839.     WSAEMFILE:
  840.       GetSocketErrorDescription := 'Too Many Open Files Failure';
  841.     WSAEWOULDBLOCK:
  842.       GetSocketErrorDescription := 'Operation Would Block Failure';
  843.     WSAEINPROGRESS:
  844.       GetSocketErrorDescription := 'Operation Blocking Failure';
  845.     WSAEALREADY:
  846.       GetSocketErrorDescription := 'Operation Already in Progress Failure';
  847.     WSAENOTSOCK:
  848.       GetSocketErrorDescription := 'Invalid Socket Operation Failure';
  849.     WSAEDESTADDRREQ:
  850.       GetSocketErrorDescription := 'No Destination Address Failure';
  851.     WSAEMSGSIZE:
  852.       GetSocketErrorDescription := 'Invalid Message Length Failure';
  853.     WSAEPROTOTYPE:
  854.       GetSocketErrorDescription := 'Invalid Protocol For Socket Failure';
  855.     WSAENOPROTOOPT:
  856.       GetSocketErrorDescription := 'Unavilable Protocol Failure';
  857.     WSAEPROTONOSUPPORT:
  858.       GetSocketErrorDescription := 'Unsupported Protocol Failure';
  859.     WSAESOCKTNOSUPPORT:
  860.       GetSocketErrorDescription := 'Unsupported Socket Type Failure';
  861.     WSAEOPNOTSUPP:
  862.       GetSocketErrorDescription := 'Unsupported Socket Operation Failure';
  863.     WSAEPFNOSUPPORT:
  864.       GetSocketErrorDescription := 'Unsupported Protocol Family Failure';
  865.     WSAEAFNOSUPPORT:
  866.       GetSocketErrorDescription := 'Invalid Protocol-Address Family Failure';
  867.     WSAEADDRINUSE:
  868.       GetSocketErrorDescription := 'Address In Use Failure';
  869.     WSAEADDRNOTAVAIL:
  870.       GetSocketErrorDescription := 'Unavailable Address Failure';
  871.     WSAENETDOWN:
  872.       GetSocketErrorDescription := 'Network Down Failure';
  873.     WSAENETUNREACH:
  874.       GetSocketErrorDescription := 'Network Unreachable Failure';
  875.     WSAENETRESET:
  876.       GetSocketErrorDescription := 'Network Connection Dropped Failure';
  877.     WSAECONNABORTED:
  878.       GetSocketErrorDescription := 'Software Abort Failure';
  879.     WSAECONNRESET:
  880.       GetSocketErrorDescription := 'Peer Connection Reset Failure';
  881.     WSAENOBUFS:
  882.       GetSocketErrorDescription := 'Buffer Overflow Failure';
  883.     WSAEISCONN:
  884.       GetSocketErrorDescription := 'Connected Socket Failure';
  885.     WSAENOTCONN:
  886.       GetSocketErrorDescription := 'Unconnected Socket Failure';
  887.     WSAESHUTDOWN:
  888.       GetSocketErrorDescription := 'Closed Socket Send Failure';
  889.     WSAETOOMANYREFS:
  890.       GetSocketErrorDescription := 'Reference Count Overflow Failure';
  891.     WSAETIMEDOUT:
  892.       GetSocketErrorDescription := 'Connection Timeout Failure';
  893.     WSAECONNREFUSED:
  894.       GetSocketErrorDescription := 'Connection Refusal Failure';
  895.     WSAELOOP:
  896.       GetSocketErrorDescription := 'Symbolic Link Overflow Failure';
  897.     WSAENAMETOOLONG:
  898.       GetSocketErrorDescription := 'Invalid File Name Failure';
  899.     WSAEHOSTDOWN:
  900.       GetSocketErrorDescription := 'Host Down Failure';
  901.     WSAEHOSTUNREACH:
  902.       GetSocketErrorDescription := 'Host Unreachable Failure';
  903.     WSAENOTEMPTY:
  904.       GetSocketErrorDescription := 'Non-Empty Directory Removal Failure';
  905.     WSAEPROCLIM:
  906.       GetSocketErrorDescription := 'Process Overflow Failure';
  907.     WSAEUSERS:
  908.       GetSocketErrorDescription := 'Users Overflow Failure';
  909.     WSAEDQUOT:
  910.       GetSocketErrorDescription := 'Disk Quota Overflow Failure';
  911.     WSAESTALE:
  912.       GetSocketErrorDescription := 'Invalid File Handle Failure';
  913.     WSAEREMOTE:
  914.       GetSocketErrorDescription := 'File Path Overflow Failure';
  915.     WSASYSNOTREADY:
  916.       GetSocketErrorDescription := 'Unavailable Sub-Network Failure';
  917.     WSAVERNOTSUPPORTED:
  918.       GetSocketErrorDescription := 'Winsock Application Compatibility Failure';
  919.     WSANOTINITIALISED:
  920.       GetSocketErrorDescription := 'WinSock Uninitialized Failure';
  921.     WSAHOST_NOT_FOUND:
  922.       GetSocketErrorDescription := 'Host Not Located Failure';
  923.     WSATRY_AGAIN:
  924.       GetSocketErrorDescription := 'Non-Authority Host Not Located Failure';
  925.     WSANO_RECOVERY:
  926.       GetSocketErrorDescription := 'Fatal Winsock Error Failure';
  927.     WSANO_DATA:
  928.       GetSocketErrorDescription := 'Data Not Available Failure';
  929.     else GetSocketErrorDescription := 'No Error';
  930.   end;
  931. end;
  932.  
  933. { Activate timeout procedure }
  934. procedure TCCSocket.ActivateNonAsynchTimeout;
  935. begin
  936.   if FTimeoutValue > 0 then
  937.     SetTimer( Handle , 10 , FTimeoutValue * 1000 , nil );
  938. end;
  939.  
  940. { Deactivate timeout procedure }
  941. procedure TCCSocket.DeactivateNonAsynchTimeout;
  942. begin
  943.   if FTimeoutValue > 0 then KillTimer( Handle , 10 );
  944. end;
  945. end.
  946.